package net

import (
	"net/http"
	"github.com/op/go-logging"
	"myPaxos/logger"
	"myPaxos/manager"
	"encoding/json"
	"sync"
	"fmt"
	"io"
)

type RequestBody []byte
const BodyTempLength = 512

type RespWriter struct {
	respBody RequestBody
	w http.ResponseWriter
}

type Server struct {
	info     string
	ID       int
	handler  func(w http.ResponseWriter, req *http.Request)
	hostname string
	logger   *logging.Logger
	read     chan *RespWriter
	routers  sync.Map
	eh       *manager.Eventhub
}

func NewServer(info string, hostname string, ID int, eh *manager.Eventhub) *Server {
	server := &Server{
		info:     info,
		ID:       ID,
		hostname: hostname,
		logger:   logger.GetLogger("server"),
		read:     make(chan *RespWriter),
		eh:       eh,
	}
	server.handler = func(w http.ResponseWriter, req *http.Request) {
		temp := make([]byte, 0)
		content := make([]byte, BodyTempLength)
		n, err := req.Body.Read(content)
		if err != nil && err != io.EOF {
			server.logger.Errorf("read remote message error: %s", err.Error())
		} else if n == BodyTempLength{
			for ;n>=0; {
				temp = append(temp, content...)
				content = make([]byte, BodyTempLength)
				n, err = req.Body.Read(content)
				if err == io.EOF {
					break
				}
			}
			temp = append(temp, content[0:n]...)
			respWriter := &RespWriter{respBody: temp, w: w}
			server.read <- respWriter
		} else {
			temp = content[0:n]
			respWriter := &RespWriter{respBody: temp, w: w}
			server.read <- respWriter
		}
		temp = nil
	}
    return server
}

func (server *Server) Start() {
	go server.eventLoop()
	mux := http.NewServeMux()
	mux.HandleFunc("/", server.handler)
	err := http.ListenAndServe(server.info, mux)
	if err != nil {
		server.logger.Fatal("server start error: %s", err.Error())
	}
}

func (server *Server) eventLoop() {
	for {
		respWriter := <- server.read
		wrapper := &manager.Wrapper{}
		err := json.Unmarshal([]byte(respWriter.respBody), wrapper)
		if err != nil { // handle Ping event
			server.logger.Errorf("server receive wrapper error: %s", err.Error())
		} else { // handle wrapper
			evPayload,err := ParseWrapper(wrapper)
			if err != nil {
				server.logger.Errorf("parse wrapper error: %s", err.Error())
			}
			switch wrapper.Type {
			case manager.Vote_Wrapper:
				fallthrough
			case manager.Msg_Wrapper:
				server.eh.PostWrapper(evPayload, wrapper.Type)
				break
			case manager.Ping_Wrapper:
				ping := evPayload.(*manager.Ping)
				clientHost := ping.Hostname
				if _,ok := server.routers.Load(clientHost); ok {
					server.logger.Errorf("receive repeated client: %s", clientHost)
				} else {
					server.routers.Store(clientHost, true)
					pong,err := json.Marshal(&manager.Pong{Hostname: server.hostname, ID: server.ID})
					if err == nil {
						server.logger.Debugf("receive ping, and send pong out")
						respWriter.w.Write(pong)
					} else {
						server.logger.Errorf("construct Pong event error: %s", err.Error())
					}
				}
			default:
				err = fmt.Errorf("error response type, unsupported payload in Wrapper: %d", wrapper.Type)
				server.logger.Errorf(err.Error())
			}
		}
	}
}
